#pragma once

#include <algorithm/shared/utils/GenericVectorMath.h>
#include <utils/config.h>


#include <type_traits>

namespace solar::internal::vector::cuda
{
    template <Struct T1, Struct T2>
    inline __device__ auto divide(T1&& vec1, T2&& vec2) -> std::remove_cvref_t<T1>
    {
        using RawT = std::remove_cvref_t<T1>;
        RawT ret{};
        forEach<boost::pfr::tuple_size_v<RawT>>(
            vec1, vec2, ret,
            [] __device__(auto& field1, auto& field2, auto& ret_field)
            {
                if constexpr (std::is_same_v<float, decltype(field1)> &&
                              std::is_same_v<float, decltype(field2)>)
                {
                    ret_field = fdividef(field1, field2);
                }
                else
                {
                    ret_field = field1 / field2;
                }
            });
        return ret;
    }

    template <Struct T1, Number T2>
    inline __device__ auto divide(T1&& vec1, T2&& num) -> std::remove_cvref_t<T1>
    {
        using RawT = std::remove_cvref_t<T1>;
        RawT ret{};
        forEach<boost::pfr::tuple_size_v<RawT>>(
            vec1, ret,
            [num] __device__(auto& field1, auto& ret_field)
            {
                if constexpr (std::is_same_v<float, decltype(field1)> &&
                              std::is_floating_point_v<std::remove_cvref_t<T2>>)
                {
                    ret_field = fdividef(field1, num);
                }
                else
                {
                    ret_field = field1 / num;
                }
            });
        return ret;
    }

    template <Struct T1, Struct T2> inline __device__ auto divideSelf(T1&& vec1, T2&& vec2) -> void
    {
        using RawT = std::remove_cvref_t<T1>;
        forEach<boost::pfr::tuple_size_v<RawT>>(
            vec1, vec2,
            [] __device__(auto& field1, auto& field2)
            {
                if constexpr (std::is_same_v<float, decltype(field1)> &&
                              std::is_same_v<float, decltype(field2)>)
                {
                    field1 = fdividef(field1, field2);
                }
                else
                {
                    field1 /= field2;
                }
            });
    }

    template <Struct T1, Number T2> inline __device__ auto divideSelf(T1&& vec1, T2&& num) -> void
    {
        boost::pfr::for_each_field(
            vec1,
            [num] __device__(auto& field1)
            {
                if constexpr (std::is_same_v<float, decltype(field1)> &&
                              std::is_floating_point_v<std::remove_cvref_t<T2>>)
                {
                    field1 = fdividef(field1, num);
                }
                else
                {
                    field1 /= num;
                }
                field1 /= num;
            });
    }
} // namespace solar::internal::vector::cuda
